package org.springframework.aop.framework.autoproxy;

import java.util.Collection;
import java.util.HashSet;
import java.util.Set;

import org.aopalliance.aop.Advice;

import org.springframework.aop.Advisor;
import org.springframework.aop.ClassFilter;
import org.springframework.aop.Pointcut;
import org.springframework.aop.TargetSource;
import org.springframework.aop.aspectj.AspectJExpressionPointcutAdvisor;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.PropertyValues;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;

/**
 * @author derekyi
 * @date 2020/12/6
 */
//这个类实现的两个接口，一个本质是 前后置方法的接口，一个是Aware（标记得到ioc的接口）
public class DefaultAdvisorAutoProxyCreator implements InstantiationAwareBeanPostProcessor, BeanFactoryAware {
//里面的代码其实都是我们直接学过的


	//1.DefaultAdvisorAutoProxyCreator这个类就是用来实现代理对象的类

	//2.InstantiationAwareBeanPostProcessor（特殊的前后置类，Spring的AOP（两种实现JDK，CGLIB）就是用前后置类实现的，毕竟前后置类可以实现类的替换）

	//3.BeanFactoryAware用于得到ioc容器，毕竟需要ioc容器得到beandefinition对象才能创建bean


	private DefaultListableBeanFactory beanFactory;

	//这个set集合是用来干嘛的，用来解决
	private Set<Object> earlyProxyReferences = new HashSet<>();

	//后置方法
	//不同实现类，我们通过后置方法失效动态代理对象生成
	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {

		if (!earlyProxyReferences.contains(beanName)) {
			//这里执行的就是动态代理生成代理对象
			return wrapIfNecessary(bean, beanName);
		}

		return bean;
	}

	//这个方法来自于特殊的前后置类（InstantiationAwareBeanPostProcessor），想要知道这个方法干嘛以及涉及到的成员变量
	//我们需要看InstantiationAwareBeanPostProcessor的源码
	@Override
	public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
		//将需要生成代理对象的bean的名称加入set集合
		earlyProxyReferences.add(beanName);

		//这个方法是返回bean的代理对象的方法
		//1.判断是否是3个特殊类，会陷入死循环
		//2.通过ioc容器，先得到所有的Advisor实现类（里面有切入点表达式和对应增强方法拦截器）
		//3.创建proxyFactory，可以选择不同创建动态代理的方式（JDK或者CGLIB）
		//4.遍历Advisor，一个个增强，当然这里的增强是对BeanFectition
		return wrapIfNecessary(bean, beanName);
	}



	//里面这个方法才是前置方法啊？
	protected Object wrapIfNecessary(Object bean, String beanName) {//我们传入的bean是准备被代理的对象
		//避免死循环，先判断一下他是不是普通的bean，如果是那三类就会出现死循环，我们要避免
		if (isInfrastructureClass(bean.getClass())) {
			return bean;
		}

		//类型匹配，找到所有是AspectJExpressionPointcutAdvisor==advisor类的。封装成集合（拦截器链前置条件）
		//所有的advisor类都要存入进来。这些类是xml文件里配置的，需要我们手动导入，不是ioc自动的
		Collection<AspectJExpressionPointcutAdvisor> advisors =
				beanFactory.getBeansOfType(AspectJExpressionPointcutAdvisor.class)
				.values();
		try {
			//ProxyFactory就是jdk还是cglib
			//proxyFactory继承AdvisedSupport接口，用于存储哪些
			ProxyFactory proxyFactory = new ProxyFactory();

			//你可能之前还疑惑为什么要有advisor类封装一下，我们通过xml文件传输后，我们就可以直接在这里面取了，不需要额外麻烦的配置
			//也就是用ioc已有的功能，来制造新的功能（通过配置属性，和创建bean。来帮助你aoc融入bean更轻松）
			for (AspectJExpressionPointcutAdvisor advisor : advisors) {//遍历所有advisor类



				//得到他匹配的类拦截器
				ClassFilter classFilter = advisor.getPointcut().getClassFilter();//xml给了数据了
				//我们传入的类要和所有的advisor类型匹配一下，如果适配就要增强（拦截器链）
				// 当然先判断类，里面会继续判断方法，毕竟我们增强的是方法
				if (classFilter.matches(bean.getClass())) {//当前bean要被代理，他符合哪个advisor的切入点表达式


					TargetSource targetSource = new TargetSource(bean);
					//bean怎么来的，不是代理对象创建的吗？记得看视频，源码和视频代码不一样
					//视频里用beandefinition对象，ioc容器调用了实例化方法（空bean）
					proxyFactory.setTargetSource(targetSource);
					proxyFactory.addAdvisor(advisor);//添加所有的拦截器,拦截器链。一个个匹配
					proxyFactory.setMethodMatcher(advisor.getPointcut().getMethodMatcher());
				}
			}
			if (!proxyFactory.getAdvisors().isEmpty()) {
				//创建代理对象的方法，只不过这里省略了JDK还是CGLIB，用了proxyFactory省略的
				return proxyFactory.getProxy();

				//返回代理对象
			}
		} catch (Exception ex) {
			throw new BeansException("Error create proxy bean for: " + beanName, ex);
		}
		return bean;
	}

	private boolean isInfrastructureClass(Class<?> beanClass) {
		//避免这仨
		return Advice.class.isAssignableFrom(beanClass)
				|| Pointcut.class.isAssignableFrom(beanClass)
				|| Advisor.class.isAssignableFrom(beanClass);
	}

	@Override
	public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
		this.beanFactory = (DefaultListableBeanFactory) beanFactory;
	}

	@Override
	public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
		//好像啥也没
		return null;
	}

	@Override
	public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
		return true;
	}

	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}

	@Override
	public PropertyValues postProcessPropertyValues(PropertyValues pvs, Object bean, String beanName) throws BeansException {
		//这里返回pvs，你在xml文件定义的属性会被写进来（原本类，被代理类的的属性）
		return pvs;
	}
}
